package liquidfs.ui.ftp;

import java.util.*;
import java.util.logging.*;

import liquidfs.*;
import liquidfs.core.*;
import liquidfs.data.ex.*;
import liquidfs.data.fs.*;

import org.apache.ftpserver.ftplet.*;

import utility.*;

public class LiquidFileSystemView implements FileSystemView {

   private Application app;
   private LiquidFileSystem fs;
   
   private LiquidDirectory root;
   private LiquidDirectory workingDir;
   
   private ParameterizedExecutor threadPool;
   
   private Map<String, LiquidDirectory> cachedDirectories = new HashMap<String, LiquidDirectory> ();
   
   private ModificationListener modificationListener = new ModificationListener () {
      public void objectModified(String path) {
         
         // Get the object at this path
         LiquidObject object = fs.getPath(path);
         
         for (LiquidDirectory dir : cachedDirectories.values()) {
            
            // If it is a cached directory, clear its cache
            if (object.getPath().equals(dir.getPath())) {
               dir.refresh();
            }
            
            // If our working directory is its parents, clear its cache
            if (object.getParentPath().equals(dir.getPath())) {
               dir.refresh();
            }
         }
      }
   };
   
   private ParameterizedRunnable<LiquidDirectory> subdirectoryCacher = new ParameterizedRunnable<LiquidDirectory> (){
      public void run(LiquidDirectory directory) {
         List<LiquidObject> contents = directory.getContents();
         
         // Cache the contents list of every sub directory one level down as well
         for (LiquidObject child : contents) {
            if (child.isDirectory()) {
               LiquidDirectory subdir = child.asDirectory();
               subdir.getContents();
               cachedDirectories.put(subdir.getPath(), subdir);
            }
         }  
      }
   };
   
   private static Logger log = Logger.getLogger("liquidfs.ui.ftp");
   
   public LiquidFileSystemView (Application app) {
      
      this.app = app;
      this.fs = app.getFileSystem();
      
      // Create a thread pool for future use
      this.threadPool = new ParameterizedCachedThreadPool();
      
      // Get the default working directory
      this.workingDir = this.root = fs.getRoot();
      
      // Cache the contents list of this directory and its sub-directories, so that we can respond quickly when asked for it on connection
      cachedDirectories.put(this.root.getPath(), this.root);
      subdirectoryCacher.run(this.root);

      // Declare interest in object modifications, so we can update our caches
      this.app.getOperationProcessor().addModificationListener(modificationListener);
      
      // Hide some log entries
      log.setLevel(Level.FINE);
   }
   
   public String convertPath (String path) {
      
      if (path.startsWith("."))
         path = path.replace(".", workingDir.getPath());
      
      while (path.contains("//"))
         path = path.replace("//", "/");
      
      if (path.endsWith("/") && path.length() > 1)
         path = path.substring(0, path.length() - 1);
      
      if (!path.startsWith("/") && workingDir.getPath().equals("/"))
         path = "/" + path;
      else if (!path.startsWith("/"))
         path = this.workingDir.getPath() + "/" + path;
         
      return path;
   }
   
   @Override
   public boolean changeWorkingDirectory(String path) throws FtpException {
      
      log.fine("changeWorkingDirectory(" + path + ")");
      path = convertPath(path);
      
      // Get the directory at the new path
      try {
         
         // Check if we're caching this object
         LiquidDirectory dir = cachedDirectories.get(path);
         
         if (dir == null) {
            dir = fs.getPath(path).asDirectory();
            cachedDirectories.put(path, dir);
         }
         
         // Set this to the working directory
         workingDir = dir;
         
         // Cache the contents of the subdirectories, in a separate thread (no need to wait on this)
         threadPool.execute(subdirectoryCacher, dir);
         
         return true;
      }
      catch (FileSystemException e) {
         return false;
      }
   }

   @Override
   public void dispose() {
      // TODO Auto-generated method stub
      
   }

   @Override
   public FtpFile getFile(String path) throws FtpException {
      log.finer("getFile(" + path + ")");      
      return new LiquidFtpFile(fs.getPath(convertPath(path)));
   }

   @Override
   public FtpFile getHomeDirectory() throws FtpException {
      log.finer("getHomeDirectory()");
      return new LiquidFtpFile(this.root);
   }

   @Override
   public FtpFile getWorkingDirectory() throws FtpException {
      log.finer("getWorkingDirectory()");
      return new LiquidFtpFile(this.workingDir);
   }

   @Override
   public boolean isRandomAccessible() throws FtpException {
      log.finer("isRandomAccessible()");
      return false;
   }

}
